﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using ERPStore.Coupons.Repositories;
using ERPStore.Extensions;

using CloudSoft.Extensions;

namespace ERPStore.Coupons.Services
{
	public class AdminIncentiveService 
	{
		private System.Collections.Concurrent.ConcurrentDictionary<string, Type> m_Providers;

		public AdminIncentiveService(Logging.ILogger logger,
			CloudSoft.Repositories.IRepository<Repositories.CouponDbContext> couponRepository,
			ERPStore.Services.IAccountService accountService,
			ERPStore.Services.ICacheService cacheService,
			ERPStore.Services.IValidatorService ValidatorService,
			Models.Settings settings
			)
		{
			this.CouponRepository = couponRepository;
			this.Logger = logger;
			this.AccountService = accountService;
			this.CacheService = cacheService;
			this.ValidatorService = ValidatorService;
			m_Providers = new System.Collections.Concurrent.ConcurrentDictionary<string, Type>();
			this.Settings = settings;
		}

		protected CloudSoft.Repositories.IRepository<Repositories.CouponDbContext> CouponRepository { get; private set; }
		protected Logging.ILogger Logger { get; private set; }
		protected ERPStore.Services.IAccountService AccountService { get; private set; }
		protected ERPStore.Services.ICacheService CacheService { get; private set; }
		protected ERPStore.Services.IValidatorService ValidatorService { get; private set; }
		protected Models.Settings Settings { get; private set; }

		public void RegisterCouponProvider(string providerName, Type couponType)
		{
			if (!m_Providers.ContainsKey(providerName))
			{
				m_Providers.TryAdd(providerName, couponType);
			}
		}

		public Models.Coupon CreateCoupon()
		{
			var coupon = new Models.Coupon();
			coupon.Code = GenerateCode();
			coupon.CreationDate = DateTime.Now;
			coupon.LastUpdate = DateTime.Now;
			coupon.MaximumUseCount = null;
			coupon.MinimumItemCount = null;
			coupon.MinimumPurchase = null;
			coupon.ExpirationDate = DateTime.Now.AddMonths(1);
			coupon.IsFree = false;
			coupon.IsCumulable = false;
			coupon.IsExclusive = false;
			coupon.AuthenticationMandatory = false;
			coupon.MaximumUserCount = 1;
			return coupon;
		}

		public IEnumerable<Models.Coupon> GetAllCoupons()
		{
			var key = "AllCoupons";
			var result = CacheService[key] as IList<Models.Coupon>;
			if (result != null)
			{
				return result;
			}

			var query = CouponRepository.Query<Repositories.Datas.CouponData>();
			var list = query.ToList();

			result = AutoMapper.Mapper.Map<IList<Models.Coupon>>(list);

			var couponProviderList = GlobalConfiguration.Configuration.DependencyResolver.GetServices<ERPStore.Services.ICouponProvider>();
			if (couponProviderList != null)
			{
				foreach (var couponProvider in couponProviderList)
				{
					var fixedCouponList = couponProvider.GetDiscountList();
					if (fixedCouponList != null)
					{
						foreach (var fixedCoupon in fixedCouponList)
						{
							var c = CreateCoupon();
							c.Code = fixedCoupon.Code;
							c.Description = fixedCoupon.DiscountReason;
							c.ProviderName = fixedCoupon.ProviderName;
							result.Add(c);
						}
					}
				}
			}

			CacheService.Add(key, result, DateTime.Now.AddDays(1));

			return result;
		}

		public IList<Models.Coupon> GetCouponList(Models.CouponListFilter filter, out int count)
		{
			var query = CouponRepository.Query<Repositories.Datas.CouponData>();
			count = 0;

			if (!filter.Search.IsNullOrTrimmedEmpty())
			{
				query = query.Where(i => i.Code == filter.Search
									|| i.Description == filter.Search
									|| i.VendorCode == filter.Search);
			}

			count = query.Count();

			if (!filter.SortByPropertyName.IsNullOrTrimmedEmpty())
			{
				if (filter.SortDirection == System.ComponentModel.ListSortDirection.Ascending)
				{
					query = query.OrderBy(filter.SortByPropertyName);
				}
				else
				{
					query = query.OrderByDescending(filter.SortByPropertyName);
				}
			}
			else
			{
				query = query.OrderByDescending(i => i.Id);
			}

			query = query.Skip(filter.PageIndex * filter.PageSize).Take(filter.PageSize);
			var list = query.ToList();
			var result = AutoMapper.Mapper.Map<IList<Models.Coupon>>(list);
			return result;
		}

		public Models.Coupon GetCouponById(int couponId)
		{
			var result = GetAllCoupons().FirstOrDefault(i => i.Id == couponId);
			return result;
		}

		public IList<ERPStore.Models.BrokenRule> ValidateCoupon(Models.Coupon model)
		{
			var br = ValidatorService.Validate(model);
			return br;
		}

		public bool IsCodeExists(string code)
		{
			var coupon = CouponRepository.Query<Repositories.Datas.CouponData>()
							.Where(i => i.Code == code)
							.SingleOrDefault();

			return coupon != null;
		}

		public void SaveCoupon(Models.Coupon coupon)
		{
			var br = ValidateCoupon(coupon);
			if (!br.IsNullOrEmpty())
			{
				throw new ERPStore.Models.ValidationException(br);
			}

			Repositories.Datas.CouponData couponData = null;
			if (coupon.Id == 0)
			{
				couponData = AutoMapper.Mapper.Map<Repositories.Datas.CouponData>(coupon);
				CouponRepository.Insert<Repositories.Datas.CouponData>(couponData);
			}
			else
			{
				couponData = CouponRepository.Get<Repositories.Datas.CouponData>(i => i.Id == coupon.Id);
				couponData = AutoMapper.Mapper.Map<Models.Coupon, Repositories.Datas.CouponData>(coupon, couponData);
				couponData.LastUpdate = DateTime.Now;
				CouponRepository.Update<Repositories.Datas.CouponData>(couponData);
			}

			coupon.Id = couponData.Id;
			coupon.Version = couponData.Version;

			string key = "AllCoupons";
			CacheService.Remove(key);
		}

		public IEnumerable<Providers.CouponProviderBase> GetCouponProviderList()
		{
			var key = "CouponProviderList";
			var result = CacheService[key] as IList<Providers.CouponProviderBase>;
			if (result != null)
			{
				return result;
			}
			result = new List<Providers.CouponProviderBase>();
			foreach (var providerName in m_Providers.Keys)
			{
				result.Add(GetProviderInstance(providerName));
			}

			CacheService.Add(key, result, DateTime.Now.AddDays(1));
			return result;
		}

		public virtual Models.Coupon GetCouponByCode(string couponCode)
		{
			if (couponCode.IsNullOrEmpty())
			{
				return null;
			}
			var coupon = GetAllCoupons().FirstOrDefault(i => i.Code.Equals(couponCode, StringComparison.InvariantCultureIgnoreCase));
			return coupon;
		}

		public Models.CartDiscount GenerateDiscount()
		{
			var cartDiscount = new Models.CartDiscount();
			cartDiscount.CreationDate = DateTime.Now;
			return cartDiscount;
		}

		public void SaveDiscount(Models.CartDiscount cartDiscount)
		{
			Repositories.Datas.DiscountData discountData = null;
			if (cartDiscount.Id == 0)
			{
				discountData = AutoMapper.Mapper.Map<Repositories.Datas.DiscountData>(cartDiscount);
				discountData.LastUpdate = DateTime.Now;
				CouponRepository.Insert<Repositories.Datas.DiscountData>(discountData);
			}
			else
			{
				discountData = CouponRepository.Get<Repositories.Datas.DiscountData>(i => i.Id == cartDiscount.Id);
				discountData = AutoMapper.Mapper.Map<Models.CartDiscount, Repositories.Datas.DiscountData>(cartDiscount, discountData);
				discountData.LastUpdate = DateTime.Now;
				CouponRepository.Update<Repositories.Datas.DiscountData>(discountData);
			}
			cartDiscount.Id = discountData.Id;
		}

		public void DeleteDiscount(Models.CartDiscount cartDiscount)
		{
			var data = CouponRepository.Get<Repositories.Datas.DiscountData>(i => i.Id == cartDiscount.Id);
			if (data != null)
			{
				CouponRepository.Delete<Repositories.Datas.DiscountData>(data);
			}
		}

		internal Providers.CouponProviderBase GetProviderInstance(string providerName)
		{
			var type = m_Providers[providerName];
			var instance = GlobalConfiguration.Configuration.DependencyResolver.GetService(type) as Providers.CouponProviderBase;
			instance.Name = providerName;
			return instance;
		}

		internal string GenerateCode()
		{
			/*
			int id = Settings.CodeStartNumber;
			var codePattern = Settings.CodePattern;
			var prefix = System.Text.RegularExpressions.Regex.Replace(codePattern, @"\{[^\}]*\}", "");
			var query = CouponRepository.Query<Repositories.Datas.CouponData>();
			query = query.Where(i => i.Code.StartsWith(prefix));
			query = query.OrderByDescending(i => i.Code);
			var lastCoupon = query.FirstOrDefault();
			if (lastCoupon != null)
			{
				var code = lastCoupon.Code;
				code = System.Text.RegularExpressions.Regex.Replace(code, @"\D", "");
				id = Convert.ToInt32(code) + 1;
			}

			var result = string.Format(codePattern, id);
			 */
			string result = null;
			while (true)
			{
				result = Guid.NewGuid().ToString().Replace("-", "").Right(6).ToUpper();
				if (!IsCodeExists(result))
				{
					break;
				}
			}
			return result;
		}

		internal void LoadCouponList(ERPStore.Models.OrderCart cart)
		{
			var datas = CouponRepository.Query<Repositories.Datas.DiscountData>()
							.Where(i => i.InternalEntityId == cart.Id)
							.Where(i => i.MetaEntityId == -53)
							.ToList();

			var list = AutoMapper.Mapper.Map<IList<Models.CartDiscount>>(datas);
			cart.DiscountList = list.Cast<ERPStore.Models.IDiscount>();
		}
	}
}
